﻿#include <QMessageBox>
#include <QFileDialog>
#include <QDebug>
#include <QSettings>
#include <QStringList>
#include <QDir>
#include <QFile>
#include <QTextStream>
#include <QChar>
#include <QPalette>
#include <QDateTime>
#include <QHash>
#include <windows.h>

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget),
    area(Widget::Init)
{
    ui->setupUi(this);

    // 将"协议"输入框的背景色改为Disabled的颜色.
    QPalette palette = ui->lineEditProtocol->palette();
    palette.setBrush(QPalette::Base,
                     palette.brush(QPalette::Disabled, QPalette::Base));    // 将Disabled组中的Base角色赋给当前组中的Base角色.
    ui->lineEditProtocol->setPalette(palette);

    // 信号关联
    connect(ui->pushButtonClose, &QPushButton::clicked,
            this, &Widget::close);                      // 关闭按钮
    connect(ui->pushButtonSave, &QPushButton::clicked,
            this, &Widget::onSaveEnv);                  // 保存环境变量
    connect(ui->pushButtonReset, &QPushButton::clicked,
            this, &Widget::onSetDefaultConfig);         // 重置按钮
    connect(ui->toolButtonPath, &QToolButton::clicked,
            this, &Widget::onSelectDir);                // 目录选择按钮
    connect(ui->lineEditPath, &QLineEdit::textChanged,
            this, &Widget::onModifyDir);          // 目录变化, 更新服务名列表


    /* QComboBox::currentIndexChanged是重载函数需要做静态类型转换
     * 注意参数的参数类型及const限制必须和函数的定义一致.
     * (QComboBox::*)是个匿名的成员函数指针, 等同如下的代码:
     * typedef void (QComboBox::*FF)(const QString &);
     * FF ff = &QComboBox::currentIndexChanged;
     */
    connect(ui->comboBoxDB,
            static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::currentIndexChanged),
            this, &Widget::onShowDbInfo);             // 当前数据库服务列表变更.

    // [增/删/改/保存/取消]----------------------------------------------
    connect(ui->pushButtonAdd,&QPushButton::clicked,
            this, &Widget::onAddDbInfo);          // 增加新的数据库服务配置

    connect(ui->pushButtonDel,&QPushButton::clicked,
            this, &Widget::onDelDbInfo);          // 删除当前的数据库服务配置

    // 修改当前服务信息
    connect(ui->lineEditHost, &QLineEdit::textEdited,
            this, &Widget::onModifyDbInfo);
    connect(ui->lineEditPort, &QLineEdit::textEdited,
            this, &Widget::onModifyDbInfo);
    connect(ui->lineEditSID, &QLineEdit::textEdited,
            this, &Widget::onModifyDbInfo);

    connect(ui->pushButtonOk, &QPushButton::clicked,
            this, &Widget::onSaveDbInfo);       // 保存服务配置信息
    connect(ui->pushButtonCancle, &QPushButton::clicked,
            this, &Widget::onCancleDbInfo);     // 取消保存本次服务配置信息的修改
    // ![增/删/改/保存/取消]----------------------------------------------

    connect(ui->pushButtonSaveFile, &QPushButton::clicked,
            this, &Widget::onSaveFile);      // 保存配置文件.

    // 设置默认配置选项
    onSetDefaultConfig();

}


Widget::~Widget()
{
    delete ui;
}

/******************************************************************************************************
 *
 * SLOT
 *
 ******************************************************************************************************/

/**
 * @brief Widget::onSelectDir
 * SLOT
 * 弹出选择目录的对话窗口
 * @return: 所选的目录路径; 如果目录无效返回空串
 *
 * 该方法响应toolButtonPath.Clicked()信号
 */
QString Widget::onSelectDir()
{
    // 选择目录
    QString path = QFileDialog::getExistingDirectory(this, tr("Open Directory"),
                                                     QApplication::applicationDirPath());
    // 如果path不为空, 更新安装目录的路径.
    if (!path.isEmpty()){
        ui->lineEditPath->setText(QDir::toNativeSeparators(path)); // QDir::​toNativeSeparators 将目录分割符转为Windows下的"\"
    }

    qDebug() << "onSelectDir::path : " << path;
    //getDbServerName();
    return path;
}


/**
 * @brief Widget::onSaveEnv
 * SLOT
 * 将目录及字符集保存到环境变量
 * HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Session Manager\Environment
 * HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
 *
 * 该方法响应pushButtonSave::Clicked()信号
 */
void Widget::onSaveEnv()
{
    QSettings env("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment",
                  QSettings::NativeFormat);                 // 取环境变量

    QString installPath = QDir::toNativeSeparators(ui->lineEditPath->text());   // 取当前设置的安装路径; QDir::​toNativeSeparators 将目录分割符转为Windows下的"\"

    qDebug() << "onSaveEnv::installPath" <<installPath;
    //设置环境变量"TNS_ADMIN"和"Path";
    if (!installPath.isEmpty()){
//        env.setValue(QStringLiteral("TNS_ADMIN"), installPath);             // 设置"TNS_ADMIN"变量
        env.remove(QStringLiteral("TNS_ADMIN"));
        setEnvValue(QStringLiteral("TNS_ADMIN"), installPath);           // 设置"TNS_ADMIN"变量
        QString envPath = env.value(QStringLiteral("Path"), "").toString(); // 取"Path"变量
        // 设置"Path"变量
        if (envPath.isEmpty()){     // "Path"变量为空
//            env.setValue(QStringLiteral("Path"), installPath + QLatin1Literal(";"));
            setEnvValue(QStringLiteral("Path"), QLatin1Literal("%TNS_ADMIN%;"));
        } else {                    // "Path"变量非空
            // 检查Path中是否已经包含了安装路径
            QStringList sl = envPath.split(QStringLiteral(";"));        // 按";"将Path分解成QStringList
            bool isExist = false;
            foreach (const QString &str, sl){
                if (str == QLatin1Literal("%TNS_ADMIN%")){
                    isExist = true;
                    break;
                }
            }

            // 不包含该路径
            if (!isExist){\
                if (envPath.right(1) != QLatin1Literal(";")){   // 检查Path最后一位是否是";"
                    envPath += ";";
                }
//                env.setValue(QStringLiteral("Path"), envPath + installPath + QLatin1Literal(";"));
                setEnvValue(QStringLiteral("Path"), envPath + QLatin1Literal("%TNS_ADMIN%;"));
            }
        }
    }
    // 设置NLS_LANG变量
    QString charset = ui->comboBoxLang->currentText();     // 取当前设置的字符集
    if (!charset.isEmpty()){
        env.setValue(QStringLiteral("NLS_LANG"), charset);
    }

    getDbServerName();

    // 提示
    QMessageBox::information(this, tr("提示"), tr("保存完成"));
}

/**
 * @brief Widget::onModifyDir
 * SLOT
 * 修改目录, 并刷新服务名列表
 *
 * 该方法响应&QLineEdit::textChanged信号
 */
void Widget::onModifyDir()
{
    QDir dir(ui->lineEditPath->text());
    if (!dir.exists()){
        QMessageBox::information(this, tr("提示"), tr("路径无效:该目录不存在"));
        return;
    }else{
        getDbServerName();
    }
}


/**
 * @brief Widget::onShowDbInfo
 * SLOT
 * 显示指定服务名的配置信息
 * @param serverName 数据库服务名
 *
 */
void Widget::onShowDbInfo(const QString &serverName)
{
    chargeControl(area);
    if (serverName.isEmpty() || !server.contains(serverName) ){
        curSName.clear();
        curProtocol.clear();
        curHost.clear();
        curPort.clear();
        curSid.clear();
        ui->lineEditAlias->clear();
        ui->lineEditProtocol->clear();
        ui->lineEditHost->clear();
        ui->lineEditPort->clear();
        ui->lineEditSID->clear();
        qDebug() << "onShowDbInfo::服务名为空或不存在!";
        return;
    }
    QString info = server[serverName];
    if (info.isEmpty()){
        curSName.clear();
        curProtocol.clear();
        curHost.clear();
        curPort.clear();
        curSid.clear();
        ui->lineEditAlias->clear();
        ui->lineEditProtocol->clear();
        ui->lineEditHost->clear();
        ui->lineEditPort->clear();
        ui->lineEditSID->clear();
        qDebug() << "onShowDbInfo::服务的配置信息不存在!";
        return;
    }

    ui->lineEditAlias->setText(serverName); // 服务名

    // 取协议
    QString protocol;
    QRegExp reProt(QStringLiteral("PROTOCOL = ([^\\)]*)"));
    reProt.indexIn(info);
    protocol = reProt.cap(1);
    qDebug() << "onShowDbInfo::protocol : " << protocol;

    QString host;
    QRegExp reHost;
    QString port;
    QRegExp rePort;
    QString sid;
    QRegExp reSid;
    changeInput(protocol);
    if (protocol == QStringLiteral("NMP")){
        // 命名管道方式
        // 设置计算机名
        reHost.setPattern(QStringLiteral("\\(SERVER = ([^\\)]*)\\)"));
        reHost.indexIn(info);
        host = reHost.cap(1);
        qDebug() << "onShowDbInfo::host : " << host;
        ui->lineEditHost->setText(host);

        // 设置管道名
        rePort.setPattern(QStringLiteral("\\(PIPE = ([^\\)]*)\\)"));
        rePort.indexIn(info);
        port = rePort.cap(1);
        qDebug() << "onShowDbInfo::port : " << port;
        ui->lineEditPort->setText(port);

        // 设置服务名
        reSid.setPattern(QStringLiteral("\\(SERVICE_NAME = ([^\\)]*)\\)"));
        reSid.indexIn(info);
        sid= reSid.cap(1);
        qDebug() << "onShowDbInfo::sid : " << sid;
        ui->lineEditSID->setText(sid);

    }else if(protocol == QStringLiteral("TCPS")){
        // SSL Tcp/Ip
        // 设置主机名
        reHost.setPattern(QStringLiteral("\\(HOST = ([^\\)]*)\\)"));
        reHost.indexIn(info);
        host = reHost.cap(1);
        qDebug() << "onShowDbInfo::host : " << host;
        ui->lineEditHost->setText(host);

        // 设置端口号
        rePort.setPattern(QStringLiteral("\\(PORT = ([^\\)]*)\\)"));
        rePort.indexIn(info);
        port = rePort.cap(1);
        qDebug() << "onShowDbInfo::port : " << port;
        ui->lineEditPort->setText(port);

        // 设置服务名
        reSid.setPattern(QStringLiteral("\\(SERVICE_NAME = ([^\\)]*)\\)"));
        reSid.indexIn(info);
        sid= reSid.cap(1);
        qDebug() << "onShowDbInfo::sid : " << sid;
        ui->lineEditSID->setText(sid);
    }else if(protocol == QStringLiteral("IPC")){
        // IPC
        // 设置主机名
        reHost.setPattern(QStringLiteral("\\(KEY = ([^\\)]*)\\)"));
        reHost.indexIn(info);
        host = reHost.cap(1);
        qDebug() << "onShowDbInfo::host : " << host;
        ui->lineEditHost->setText(host);

        // 设置服务名
        reSid.setPattern(QStringLiteral("\\(SID = ([^\\)]*)\\)"));
        reSid.indexIn(info);
        sid= reSid.cap(1);
        qDebug() << "onShowDbInfo::sid : " << sid;
        ui->lineEditSID->setText(sid);
    }else if(protocol == QStringLiteral("TCP")){
        // Tcp/Ip
        // 设置主机名
        reHost.setPattern(QStringLiteral("\\(HOST = ([^\\)]*)\\)"));
        reHost.indexIn(info);
        host = reHost.cap(1);
        qDebug() << "onShowDbInfo::host : " << host;
        ui->lineEditHost->setText(host);

        // 设置端口号
        rePort.setPattern(QStringLiteral("\\(PORT = ([^\\)]*)\\)"));
        rePort.indexIn(info);
        port = rePort.cap(1);
        qDebug() << "onShowDbInfo::port : " << port;
        ui->lineEditPort->setText(port);

        // 设置服务名
        reSid.setPattern(QStringLiteral("\\(SERVICE_NAME = ([^\\)]*)\\)"));
        reSid.indexIn(info);
        sid= reSid.cap(1);
        qDebug() << "onShowDbInfo::sid : " << sid;
        ui->lineEditSID->setText(sid);
    }
    qDebug() << "onShowDbInfo::area" << area;
    curSName = serverName;
    curProtocol = protocol;
    curHost = ui->lineEditHost->text().trimmed().toUpper();
    curPort = ui->lineEditPort->text().trimmed().toUpper();
    curSid = ui->lineEditSID->text().trimmed().toUpper();
}

/**
 * @brief Widget::onAddDbInfo
 * SLOT
 * 新增服务配置
 *
 * 该方法响应pushButtonAdd::clicked信号
 */
void Widget::onAddDbInfo()
{
    if (area!=Widget::Enable) return;

    ui->lineEditAlias->setReadOnly(false);
    ui->lineEditAlias->setText(QStringLiteral(""));
    ui->lineEditProtocol->setText(QStringLiteral("TCP/IP(TCP)"));
    ui->lineEditHost->setReadOnly(false);
    ui->lineEditHost->setText(QStringLiteral(""));
    ui->lineEditPort->setReadOnly(false);
    ui->lineEditPort->setText(QStringLiteral(""));
    ui->lineEditSID->setReadOnly(false);
    ui->lineEditSID->setText(QStringLiteral(""));

    changeStatus(Widget::Add);
}

/**
 * @brief Widget::onDelDbInfo
 * SLOT
 * 删除当前的服务配置信息
 *
 * 该方法响应pushButtonDel::clicked信号
 */
void Widget::onDelDbInfo()
{
    if (area!=Widget::Enable) return;

    QString serverName = ui->lineEditAlias->text().trimmed().toUpper();
    if (serverName.isEmpty()){
        qDebug() << "(ERROR -- )onDelDbInfo::serverName" << "服务名为空.";
        QMessageBox::about(this, tr("删除失败"), tr("服务名为空, 不能删除"));
        return;
    }

    //    if (curProtocol != QStringLiteral("TCP")){
    //        qDebug() << "(ERROR -- )onDelDbInfo::protocol" << "当前服务不是TCP协议,不能删除";
    //        QMessageBox::about(this, tr("删除失败"), tr("当前服务不是TCP协议,不能删除"));
    //        return;
    //    }

    changeStatus(Widget::Del);
}

/**
 * @brief Widget::onModifyDbInfo
 * SLOT
 * 修改当前服务的配置信息
 *
 * 该方法响应lineEditAlias/lineEditHost/lineEditPort/lineEditSID ::
 */
void Widget::onModifyDbInfo()
{

    if (area != Widget::Enable){
        return;
    }

    changeStatus(Widget::Update);

    if (curProtocol != QLatin1Literal("TCP") &&
            area == Widget::Update){
        QMessageBox::about(this, tr("提示"), tr("只能维护TCP协议的数据"));
        onCancleDbInfo();
        return;
    }
}

/**
 * @brief Widget::onSaveDbInfo
 * SLOT
 * 保存当前服务的配置信息
 *
 * 该方法响应pushButtonOk::clicked()信号
 */
void Widget::onSaveDbInfo()
{
    switch(area){
        case Widget::Add :{
            // 检查服务别名是否为空
            QString sName = ui->lineEditAlias->text().trimmed().toUpper();
            if (sName.isEmpty()){
                QMessageBox::about(this, tr("保存失败"), tr("别名不能为空"));
                return;
            }
            // 检查服务是否已经存在
            if (server.contains(sName)){
                QMessageBox::about(this, tr("保存失败"), tr("别名已存在,请重新输入一个新的别名"));
                return;
            }
            // 检查主机名是否为空
            QString host = ui->lineEditHost->text().trimmed().toUpper();
            if (host.isEmpty()){
                QMessageBox::about(this, tr("保存失败"), tr("主机名不能为空"));
                return;
            }
            // 检查端口号是否为空
            QString port = ui->lineEditPort->text().trimmed().toUpper();
            if (port.isEmpty()){
                QMessageBox::about(this, tr("保存失败"), tr("端口号不能为空"));
                return;
            }
            // 检查服务名是否为空
            QString sid = ui->lineEditSID->text().trimmed().toUpper();
            if (port.isEmpty()){
                QMessageBox::about(this, tr("保存失败"), tr("服务名不能为空"));
                return;
            }
            // 新增服务
            if (!addDbServerName(sName)){
                QMessageBox::about(this, tr("保存失败"), tr("新增服务失败"));
                return;
            }
            break;
        }
        case Widget::Del :{
            // 删除服务
            if (!delDbServerName()){
                QMessageBox::about(this, tr("保存失败"), tr("删除服务失败"));
                return;
            }
            break;
        }
        case Widget::Update :{

            // 检查主机名是否为空
            QString host = ui->lineEditHost->text().trimmed().toUpper();
            if (host.isEmpty()){
                QMessageBox::about(this, tr("保存失败"), tr("主机名不能为空"));
                return;
            }
            // 检查端口号是否为空
            QString port = ui->lineEditPort->text().trimmed().toUpper();
            if (port.isEmpty()){
                QMessageBox::about(this, tr("保存失败"), tr("端口号不能为空"));
                return;
            }
            // 检查服务名是否为空
            QString sid = ui->lineEditSID->text().trimmed().toUpper();
            if (port.isEmpty()){
                QMessageBox::about(this, tr("保存失败"), tr("服务名不能为空"));
                return;
            }

            // 修改服务
            if (!updateDbServerConfigInfo()){
                QMessageBox::about(this, tr("保存失败"), tr("修改服务失败"));
                return;
            }
            break;
        }
        default:{
            onShowDbInfo(curSName);
            return;
            break;
        }
    }
    changeStatus(Widget::Enable);
}

/**
 * @brief Widget::onCancleDbInfo
 * SLOT
 * 取消当前的修改
 */
void Widget::onCancleDbInfo()
{
    changeStatus(Widget::Enable);
    onShowDbInfo(curSName);
    return;
}

/**
 * @brief Widget::onSaveFile
 * 保存配置文件
 */
void Widget::onSaveFile()
{
    QString dbConfigFilePath = getDbConfigPath().append(QLatin1Literal("/tnsnames.ora")) ;  // 配置文件名
    // 读取tnsnames.ora文件.
    QFile file(dbConfigFilePath);
    if (!file.open(QIODevice::ReadWrite)){
        QMessageBox::warning(this, tr("警告"), tr("文件打开失败!"));
        return;
    }

    QString fText = file.readAll(); // 取现在的配置文件文本
    file.close();
    // 对比文本,如没有改动,则退出.
    if (fText == fileText){
        QMessageBox::about(this, tr("提示"), tr("配置信息无改动,不需要保存."));
        return;
    }


    QString bakFileName = dbConfigFilePath;      // 备份文件名
    bakFileName.remove(QStringLiteral(".ora"));
    bakFileName.append(QDateTime::currentDateTime().toString(QStringLiteral("yyyy_MM_dd"))).append(QStringLiteral(".ora_bak"));
    qDebug() << "delDbServerName::newFileName" << bakFileName;
    QFile(bakFileName).remove(); // 删除已有的备份文件
    /* 1. 备份文件(改文件名)
     * 2. 将配置信息写入文件
     */
    if (file.rename(bakFileName)){
        // 写入新文件.
        QFile fileNew(dbConfigFilePath);
        if (!fileNew.open(QIODevice::WriteOnly)){
            QMessageBox::warning(this, tr("警告"), tr("打开写文件时失败!"));
            return;
        }
        QTextStream fout(&fileNew);
        fout << fileText;
    }else{
        QMessageBox::warning(this, tr("警告"), tr("文件名修改失败!"));
        return;

    }
    QMessageBox::about(this, tr("提示"), tr("文件保存成功!"));
}

/**
 * @brief Widget::setEnvValue
 * @param keyPath  键的路径
 * @param key      键名
 * @param value    键值
 * 写环境变量
 * HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment
 * @return 是否成功
 */
bool Widget::setEnvValue(const QString &key, const QString &keyValue)
{
        HKEY hkey;
        LPCSTR subkey = "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment";
        if (ERROR_SUCCESS != RegOpenKeyExA(HKEY_LOCAL_MACHINE,subkey,0,KEY_ALL_ACCESS, &hkey)){
            QMessageBox::about(this, tr("提示"), tr("注册表打开出错."));
            return false;
        }

        LPCSTR keyName = key.toLatin1().constData();
        char * value = NULL;
        value = qstrdup(keyValue.toLatin1().data());

        DWORD valueLen = strlen(keyValue.toLatin1().data())+ 1;
        if (ERROR_SUCCESS !=RegSetValueExA(hkey, keyName,0,REG_EXPAND_SZ, (const LPBYTE)value,valueLen)){
            delete[] value;
            QMessageBox::about(this, tr("提示"), tr("注册表写入失败"));
            return false;
        }
        delete[] value;
        return true;
}


/**
 * @brief Widget::onSetDefaultConfig
 * SLOT
 * 设置默认的配置项
 *
 * 该方法响应重置按钮的Clicked()信号
 */
void Widget::onSetDefaultConfig()
{
    QComboBox* qcbLang = ui->comboBoxLang;
    // 设置当前路径为默认值
    ui->lineEditPath->setText(QApplication::applicationDirPath());
    // 清空列表
    if (qcbLang->count() > 0){
        qcbLang->clear();
    }
    // 添加字符集列表
    qcbLang->addItem(QStringLiteral("SIMPLIFIED CHINESE_CHINA.ZHS16GBK"), QVariant(qcbLang->count()));
    qcbLang->addItem(QStringLiteral("AMERICAN_AMERICA.ZHS16GBK"), QVariant(qcbLang->count()));
    qcbLang->addItem(QStringLiteral("AMERICAN_AMERICA.AL32UTF8"), QVariant(qcbLang->count()));
    qcbLang->addItem(QStringLiteral("AMERICAN_AMERICA.WE8ISO8859P1"), QVariant(qcbLang->count()));
    // 设置第一条item为默认值
    qcbLang->setCurrentIndex(0);
}


/********************************************************************************************************
 *
 * Function
 *
 * ******************************************************************************************************/

/**
 * @brief Widget::getDbServerName
 * 取服务名列表
 * @return: 服务名的列表
 */
QStringList Widget::getDbServerName()
{
    QStringList dbNames;
    QString dbConfigFilePath = getDbConfigPath().append(QLatin1Literal("/tnsnames.ora")) ;  // 配置文件路径
    // 设置控件状态
    changeStatus(Widget::Enable);
    // 读取tnsnames.ora文件.
    QFile file(dbConfigFilePath);
    if (!file.open(QIODevice::ReadOnly)){
        QMessageBox::warning(this, tr("警告"), tr("文件打开失败!"));
        changeStatus(Widget::Disable);
        return dbNames;
    }
    QTextStream fin(&file);
    fileText = fin.readAll();   // 读取全文
    fin.seek(0);
    qDebug() << "Widget::fileText" << fileText;
    QRegExp re(QStringLiteral("^[^\\(\\)].* =$"));   // 匹配服务名的行
    while (!fin.atEnd()) {
        QString line = fin.readLine().trimmed();    // 取文件行,并去掉头尾空格
        int pos =  re.indexIn(line);
        if (pos > -1){
            QString serverName = re.cap();
            QString nextLine = fin.readLine().trimmed();    // 取服务名的下一行
            if (nextLine == QStringLiteral("(DESCRIPTION =")){
                serverName = serverName.left(serverName.length() - 2);
                qDebug() << "getDbServerName::serverName : " << serverName;
                dbNames.append(serverName);
            }
        }
    }
    file.close();
    QStringListIterator iteraor(dbNames);
    while(iteraor.hasNext()){
        QString sName = iteraor.next();
        server[sName] = getDbServerConfigInfo(sName);
        qDebug() << "Widget::Server["<< sName << "] : " << server[sName];
    }
    ui->comboBoxDB->clear();
    ui->comboBoxDB->addItems(dbNames);

    return dbNames;
}

/**
 * @brief Widget::getDbConfigPath
 * 取数据库配置文件(tnsnames.ora)路径
 *
 * @return 数据库配置文件路径
 */
QString Widget::getDbConfigPath()
{
    QSettings env(QStringLiteral("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"),
                  QSettings::NativeFormat);                 // 取环境变量
    QString dbConfigPath = QDir::fromNativeSeparators(env.value(QStringLiteral("TNS_ADMIN"),
                                                                ui->lineEditPath->text()).toString());  // 取tnsnames.ora文件的位置.
    qDebug()<< "getDbConfigPath::dbConfigPath" << dbConfigPath;

    return dbConfigPath;
}

/**
 * @brief Widget::getDbServerConfigInfo
 * 取数据库服务配置信息
 * step1: 找到服务名所在行
 * step2: 从服务名所在行的下一行开始取文本,
 *        要求文本必须以"("或")"开头,
 *        将取到的文本拼成一行保存在QString中
 * step3: 从保存的QString中取出协议名,
 *        根据协议取相关配置,
 *        将配置放入到文本框中
 * @param serverName 数据库服务名
 * @return 拼接好的服务的配置信息字符串
 */
QString Widget::getDbServerConfigInfo(const QString &serverName)
{
    QString dbConfigFilePath = getDbConfigPath().append(QLatin1Literal("/tnsnames.ora")) ;  // 配置文件路径
    // 读取tnsnames.ora文件.
    QFile file(dbConfigFilePath);
    if (!file.open(QIODevice::ReadOnly)){
        QMessageBox::warning(this, tr("警告"), tr("文件打开失败!"));
        return QString();
    }

    QTextStream fin(&file);

    QString info;   // 保存该服务的配置信息
    bool flag = true;  // 匹配成功标志, true:信息未获取完, false:信息获取完成.
    while (!fin.atEnd() && flag) {
        QString line = fin.readLine();    // 取文件行
        if (line == (serverName + QStringLiteral(" ="))){
            info.append(line).append("\r\n");
            // 匹配成功,服务名已找到, 从服务名的下一行开始取配置信息行
            QRegExp reInfo(QStringLiteral("^\\s*[\\(\\)].*$"));     //匹配服务的配置信息行, 要求以"("或")"开头的行
            while(flag){
                // 将该服务的配置信息拼接后保存到info中
                QString nextLine = fin.readLine();
                int posInfo = reInfo.indexIn(nextLine);
                if (posInfo > -1){
                    info.append(reInfo.cap()).append("\r\n");
                }else{
                    flag = false;   // 匹配失败, 配置信息获取结束.
                }
            }
            qDebug() << "getDbServerConfigInfo::info :" << info;
        }
    }
    file.close();
    if (info.isEmpty()){
        qDebug() << "getDbServerConfigInfo::服务配置信息为空!";
    }
    return info;
}

/**
 * @brief Widget::addDbServerName
 * @param serverName  新增的服务名
 * 增加服务配置
 *
 * @return  是否成功
 */
bool Widget::addDbServerName(const QString &serverName)
{
    // 取配置信息
    if (serverName.isEmpty()){
        QMessageBox::about(this, tr("Error"), tr("参数为空."));
        qDebug() << "Error -- addDbServerName" << "参数为空.";
        return false;
    }
    QString protocol = QStringLiteral("TCP");
    QString host = ui->lineEditHost->text().trimmed().toUpper();
    QString port = ui->lineEditPort->text().trimmed().toUpper();
    QString sid = ui->lineEditSID->text().trimmed().toUpper();

    // 拼接配置串
    QString srvInfo;
    srvInfo.append(QString("%1 =\r\n").arg(serverName));
    srvInfo.append("  (DESCRIPTION =\r\n");
    srvInfo.append("    (ADDRESS_LIST =\r\n");
    srvInfo.append(QString("      (ADDRESS = (PROTOCOL = %1)(HOST = %2)(PORT = %3))\r\n").arg(protocol,host,port));
    srvInfo.append("    )\r\n");
    srvInfo.append("    (CONNECT_DATA =\r\n");
    srvInfo.append(QString("      (SERVICE_NAME = %1)\r\n").arg(sid));
    srvInfo.append("    )\r\n");
    srvInfo.append("  )\r\n");
    srvInfo.append("\r\n");
    qDebug() << "addDbServerName::srvInfo : " << srvInfo;

    // 加入servre
    server[serverName] = srvInfo;

    // 拼接配置文件
    fileText.append("\r\n").append(srvInfo);

    // 将服务加入服务下拉列表
    ui->comboBoxDB->addItem(serverName);

    // 修改列表当前项为新增的服务.
    int index = ui->comboBoxDB->findText(serverName);
    ui->comboBoxDB->setCurrentIndex(index);

    return true;
}

/**
 * @brief Widget::delDbServerName
 * @param serverName = QString() 服务名
 * 删除指定的服务配置
 *
 * @return 是否成功
 */
bool Widget::delDbServerName(const QString &serverName)
{
    QString sName;
    // 取配置信息
    if (serverName.isEmpty()) {
        sName = curSName;
    } else{
        sName = serverName;
    }

    if (sName.isEmpty()){
        QMessageBox::about(this, tr("Error"), tr("服务名为空."));
        qDebug() << "Error -- addDbServerName" << "服务名为空.";
        return false;
    }

    QString srvInfo = server[sName];
    QString sFText = fileText;
    fileText.remove(srvInfo);   // 删除服务信息
    if (sFText == fileText){
        QMessageBox::about(this, tr("Error"), tr("删除失败"));
        return false;
    }

    server.remove(sName);  // 删除Hash表
    int index = ui->comboBoxDB->findText(sName);
    ui->comboBoxDB->removeItem(index);

    return true;
}


/**
 * @brief Widget::updateDbServerConfigInfo
 * 修改当前的服务配置信息
 *
 * @return 是否成功
 */
bool Widget::updateDbServerConfigInfo()
{
    // 取配置信息
    if (curSName.isNull()){
        QMessageBox::about(this, tr("Error"), tr("参数为空."));
        qDebug() << "Error -- addDbServerName" << "参数为空.";
        return false;
    }

    QString protocol = curProtocol;
    QString host = ui->lineEditHost->text().trimmed().toUpper();
    QString port = ui->lineEditPort->text().trimmed().toUpper();
    QString sid = ui->lineEditSID->text().trimmed().toUpper();

    if (host == curHost && port == curPort && sid ==curSid) return true;    // 未修改

    QString sFText = fileText;  // 原文件文本
    QString sInfo = server[curSName];   // 原服务信息文本
    QString newInfo = sInfo;    // 新的服务信息文本

    // 替换修改的信息.
    QRegExp re(QStringLiteral("(\\(HOST\\s*=\\s*)([^\\s\\)]*)(\\))"));
    re.indexIn(newInfo);
    qDebug() << "####################" << endl << re.cap(0) << endl;
    qDebug() << QString("\\1%1\\3").arg(host);

    newInfo.replace(QRegExp(QStringLiteral("(\\(HOST[\\s]*=[\\s]*)([^\\s\\)]*)(\\))")),
                    QString("\\1%1\\3").arg(host));
    newInfo.replace(QRegExp(QStringLiteral("(\\(PORT[\\s]*=[\\s]*)([^\\s\\)]*)(\\))")),
                    QString("\\1%1\\3").arg(port));
    newInfo.replace(QRegExp(QStringLiteral("(\\(SERVICE_NAME[\\s]*=[\\s]*)([^\\s\\)]*)(\\))")),
                    QString("\\1%1\\3").arg(sid));
    qDebug() << "updateDbServerConfigInfo::newInfo : " << newInfo;

    fileText.replace(sInfo,newInfo);    // 将文件中的原服务信息替换为新的服务信息文本

    // 对比修改前后的文件文本
    if (sFText == fileText){
        QMessageBox::about(this, tr("Error"), tr("修改失败"));
        return false;
    }

    server[curSName] = newInfo;
    onShowDbInfo(curSName);

    return true;
}



/**
 * @brief Widget::charge_control
 * @param area_status 区域状态
 * 控制各控件的状态
 */
void Widget::chargeControl(area_status status)
{

    switch(status){
        case Widget::Init:
        case Widget::Disable:
            ui->lineEditAlias->setReadOnly(true);
            ui->lineEditHost->setReadOnly(true);
            ui->lineEditPort->setReadOnly(true);
            ui->lineEditSID->setReadOnly(true);
            ui->pushButtonAdd->setEnabled(false);
            ui->pushButtonDel->setEnabled(false);
            ui->pushButtonOk->setEnabled(false);
            ui->pushButtonCancle->setEnabled(false);
            ui->pushButtonSaveFile->setEnabled(false);
            break;
        case Widget::Enable:{
            ui->lineEditAlias->setReadOnly(true);
            ui->lineEditHost->setReadOnly(false);
            ui->lineEditPort->setReadOnly(false);
            ui->lineEditSID->setReadOnly(false);
            ui->pushButtonAdd->setEnabled(true);
            ui->pushButtonDel->setEnabled(true);
            ui->pushButtonOk->setEnabled(false);
            ui->pushButtonCancle->setEnabled(false);
            ui->pushButtonSaveFile->setEnabled(true);
            break;
        }
        case Widget::Add:
            ui->lineEditAlias->setReadOnly(false);
            ui->lineEditHost->setReadOnly(false);
            ui->lineEditPort->setReadOnly(false);
            ui->lineEditSID->setReadOnly(false);
            ui->pushButtonAdd->setEnabled(false);
            ui->pushButtonDel->setEnabled(false);
            ui->pushButtonOk->setEnabled(true);
            ui->pushButtonCancle->setEnabled(true);
            ui->pushButtonSaveFile->setEnabled(false);
            break;
        case Widget::Del:
            ui->lineEditAlias->setReadOnly(true);
            ui->lineEditHost->setReadOnly(true);
            ui->lineEditPort->setReadOnly(true);
            ui->lineEditSID->setReadOnly(true);
            ui->pushButtonAdd->setEnabled(false);
            ui->pushButtonDel->setEnabled(false);
            ui->pushButtonOk->setEnabled(true);
            ui->pushButtonCancle->setEnabled(true);
            ui->pushButtonSaveFile->setEnabled(false);
            break;
        case Widget::Update:
            ui->lineEditAlias->setReadOnly(true);
            ui->lineEditPort->setReadOnly(false);
            ui->lineEditSID->setReadOnly(false);
            ui->lineEditHost->setReadOnly(false);
            ui->pushButtonAdd->setEnabled(false);
            ui->pushButtonDel->setEnabled(false);
            ui->pushButtonOk->setEnabled(true);
            ui->pushButtonCancle->setEnabled(true);
            ui->pushButtonSaveFile->setEnabled(false);
            break;
    }

    changeInput(curProtocol);
}


/**
 * @brief Widget::change_status
 * 控制状态变更
 *
 * @return 是否成功变更
 */
bool Widget::changeStatus(area_status status)
{

    // 变更状态与当前状态相同
    if (area == status){
        chargeControl(status);
        return true;
    }

    switch (area) {
        case Widget::Init :
            if (status != Widget::Enable)
                return false;
            break;
        case Widget::Enable :
            break;
        case Widget::Disable :
            if (status != Widget::Enable)
                return false;
            break;
        default:
            if (status != Widget::Enable)
                return false;
            break;
    }

    chargeControl(status);
    area = status;
    return true;
}

/**
 * @brief Widget::changeInput *
 * @param protocol  服务配置的协议.
 * 按协议设置控件的状态
 */
void Widget::changeInput(const QString protocol)
{
    if (protocol == QStringLiteral("NMP")){
        // 命名管道方式
        ui->lineEditProtocol->setText(tr("命名管道(NMP)"));  // 设置协议名
        ui->labelHost->setText(tr("主机名:"));
        ui->labelPort->setText(tr("管道名:"));
        ui->labelPort->setVisible(true);
        ui->lineEditPort->setVisible(true);
        ui->lineEditPort->setInputMask(QStringLiteral(""));

    }else if(protocol == QStringLiteral("TCPS")){
        // SSL Tcp/Ip
        ui->lineEditProtocol->setText(tr("使用SSL的TCP/IP(TCPS)"));  // 设置协议名
        ui->labelHost->setText(tr("主机名:"));
        ui->labelPort->setText(tr("端口号:"));
        ui->labelPort->setVisible(true);
        ui->lineEditPort->setVisible(true);
        ui->lineEditPort->setInputMask(QStringLiteral("99999"));

    }else if(protocol == QStringLiteral("IPC")){
        // IPC
        ui->lineEditProtocol->setText(tr("IPC(IPC)"));  // 设置协议名
        ui->labelHost->setText(tr("密钥名称:"));
        ui->labelPort->setText(tr("端口号:"));
        ui->labelPort->setVisible(false);
        ui->lineEditPort->setVisible(false);
        ui->lineEditPort->setInputMask(QStringLiteral(""));

    }else{
        // Tcp/Ip
        ui->lineEditProtocol->setText(tr("TCP/IP(TCP)"));  // 设置协议名
        ui->labelHost->setText(tr("主机名:"));
        ui->labelPort->setText(tr("端口号:"));
        ui->labelPort->setVisible(true);
        ui->lineEditPort->setVisible(true);
        ui->lineEditPort->setInputMask(QStringLiteral("00000"));
    }
}
